/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.tree.view;

import java.awt.CardLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.util.ResourceBundle;
import javax.swing.DropMode;
import javax.swing.JComponent;
import javax.swing.JTree;
import javax.swing.TransferHandler;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.text.Document;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import offset.nodes.client.model.ActivePanelContainer;
import offset.nodes.client.model.Initializable;
import offset.nodes.client.model.TreeNodeUserObject;
import offset.nodes.client.tree.model.DetailPanelTreeConfiguration;
import offset.nodes.client.tree.model.TreeNodePanel;
import offset.nodes.client.tree.model.TreeNodePanelContainer;
import offset.nodes.client.tree.model.DetailPanelTreeNodeType;

/**
 *
 * @author  Walter Lütgenau
 */
public class DetailPanelTreeConfiguratorPanel extends javax.swing.JPanel implements TreeNodePanelContainer, Initializable {

    DetailPanelTreeNodeType[] nodeTypes;
    DndButton[] typeButtons;
    DetailPanelTreeConfiguration treeModel;
    boolean displayMenu = true;
    boolean displayToolbar = true;
    public static final int MENU_VIEW = 0;
    /** A return status code - returned if Cancel button has been pressed */
    public static final int RET_CANCEL = 0;
    /** A return status code - returned if OK button has been pressed */
    public static final int RET_OK = 1;
    private int returnStatus = RET_CANCEL;
    boolean initialized = false;

    class ConfiguratorTransferHandler extends TransferHandler {

        @Override
        protected void exportDone(JComponent source, Transferable data, int action) {
            System.out.println(action);
        }

        @Override
        protected Transferable createTransferable(JComponent c) {
            return new StringSelection("node");
        }

        @Override
        public int getSourceActions(JComponent c) {
            return COPY_OR_MOVE;
        }

        @Override
        public boolean canImport(javax.swing.TransferHandler.TransferSupport transfersupport) {
            if (!transfersupport.isDrop()) {
                return false;
            }

            if (!transfersupport.isDataFlavorSupported(DataFlavor.stringFlavor)) {
                return false;
            } else {
                javax.swing.JTree.DropLocation droplocation = (javax.swing.JTree.DropLocation) transfersupport.getDropLocation();
                boolean ret = droplocation.getPath() != null;
                return ret;
            }
        }

        @Override
        public boolean importData(javax.swing.TransferHandler.TransferSupport transfersupport) {
            if (!canImport(transfersupport)) {
                return false;
            }

            javax.swing.JTree.DropLocation droplocation = (javax.swing.JTree.DropLocation) transfersupport.getDropLocation();
            TreePath treepath = droplocation.getPath();
            int i = droplocation.getChildIndex();
            String nodeTypeName;
            DetailPanelTreeNodeType nodeType = null;

            try {
                nodeTypeName = (String) transfersupport.getTransferable().getTransferData(DataFlavor.stringFlavor);
                nodeType = getNodeType(nodeTypeName);
                if (nodeType == null) {
                    return false;
                }
            } catch (UnsupportedFlavorException unsupportedflavorexception) {
                return false;
            } catch (IOException ioexception) {
                return false;
            }

            if (i == -1) {
                i = tree.getModel().getChildCount(treepath.getLastPathComponent());
            }

            DefaultMutableTreeNode sourceNode = (DefaultMutableTreeNode) treepath.getLastPathComponent();
            treeModel.insertNodeAt(sourceNode, i, nodeTypeName);

            return true;
        }
    }
    
    /** Creates new form TreeConfiguratorPanel */
    public DetailPanelTreeConfiguratorPanel(DetailPanelTreeConfiguration configuration) {
        this(configuration, true, true);
    }

    /** Creates new form TreeConfiguratorPanel */
    public DetailPanelTreeConfiguratorPanel(DetailPanelTreeConfiguration configuration, boolean displayMenu, boolean displayToolbar) {
        this.displayMenu = displayMenu;
        this.displayToolbar = displayToolbar;
        initDisplay(configuration);
    }
    
    protected void initDisplay(DetailPanelTreeConfiguration configuration) {

        initComponents();

        if (displayMenu && displayToolbar) {
            headerPanel.add(menuPanel, java.awt.BorderLayout.NORTH);
            headerPanel.add(toolBar, java.awt.BorderLayout.SOUTH);
            add(headerPanel, java.awt.BorderLayout.NORTH);
        }
        else if ( displayMenu ) {
            add(menuPanel, java.awt.BorderLayout.NORTH);
        }
        else if ( displayToolbar ) {
            add(toolBar, java.awt.BorderLayout.NORTH);
        }


        // additional initializations not possible above
        //getRootPane().setDefaultButton(okButton);
        ((CardLayout) mainPanel.getLayout()).show(mainPanel, "tree");

        initNodeTypes(configuration.getNodeTypes());
        this.treeModel = configuration;

        tree.setTransferHandler(new ConfiguratorTransferHandler());
        tree.setCellRenderer(new TreeNodeTypeRenderer(configuration.getNodeTypes()));

        if (displayToolbar) {
            tree.setDragEnabled(true);
            tree.setDropMode(DropMode.ON_OR_INSERT);
        }

        TreeSelectionListener listener = new TreeSelectionListener() {

            public void valueChanged(TreeSelectionEvent e) {
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.getPath().getLastPathComponent();
                TreeNodeUserObject userObject = (TreeNodeUserObject) node.getUserObject();
                if (userObject == null) {
                    return;
                }

                for (int i = 0; i < nodeTypes.length; i++) {
                    if (nodeTypes[i].getName().equals(userObject.getType())) {
                        TreeNodePanel panel = (TreeNodePanel) nodeTypes[i].getDetailPanel();
                        if (panel == null) {
                            return;
                        }
                        panel.setPanel(userObject);
                        break;
                    }
                }
                ((CardLayout) propertiesPanel.getLayout()).show(propertiesPanel, userObject.getType());
            }
        };

        tree.addTreeSelectionListener(listener);

    }

    public DetailPanelTreeConfiguration getTreeModel() {
        return treeModel;
    }

    /**
     * Init the toolbar buttons for the node types
     * @param nodeTypes set a drag and drop button for each node type
     */
    public void initNodeTypes(DetailPanelTreeNodeType[] nodeTypes) {
        this.nodeTypes = nodeTypes;

        if (nodeTypes[0].getDetailPanel() != null) { // set root panel
            propertiesPanel.add(nodeTypes[0].getDetailPanel(), nodeTypes[0].getName());
            ((TreeNodePanel) nodeTypes[0].getDetailPanel()).setContainer(this);
        }

        typeButtons = new DndButton[nodeTypes.length];
        for (int i = 1; i < nodeTypes.length; i++) {
            typeButtons[i] = new DndButton(nodeTypes[i].getName());
            typeButtons[i].setIcon(nodeTypes[i].getIcon());
            typeButtons[i].setToolTipText(nodeTypes[i].getName());
            typeButtons[i].setRolloverEnabled(true);

            toolBar.add(typeButtons[i]);

            propertiesPanel.add(nodeTypes[i].getDetailPanel(), nodeTypes[i].getName());
            ((TreeNodePanel) nodeTypes[i].getDetailPanel()).setContainer(this);
        }
    }

    protected DetailPanelTreeNodeType getNodeType(String name) {
        for (int i = 0; i < nodeTypes.length; i++) {
            if (nodeTypes[i].getName().equals(name)) {
                return nodeTypes[i];
            }
        }
        return null;
    }

    /**
     * Init the tree model, e.g. read tree configuration from the server
     */
    public void init(ActivePanelContainer container) {
        if (!initialized) {
            treeModel.init(tree, toolBar, menuBar);
        //initialized = true;
        }
    }

    /**
     * Find the node type of the given node.
     * @param node
     * @return the node type
     */
    protected DetailPanelTreeNodeType findNodeType(DefaultMutableTreeNode node) {
        TreeNodeUserObject userObject = (TreeNodeUserObject) node.getUserObject();
        if (userObject == null) {
            return null;
        }

        for (int i = 0; i < nodeTypes.length; i++) {
            if (nodeTypes[i].getName().equals(userObject.getType())) {
                return nodeTypes[i];
            }
        }

        return null;
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        headerPanel = new javax.swing.JPanel();
        menuPanel = new javax.swing.JPanel();
        toolBarPanel = new javax.swing.JPanel();
        toolBar = new javax.swing.JToolBar();
        deleteButton = new javax.swing.JButton();
        viewButtonGroup = new javax.swing.ButtonGroup();
        menuBar = new javax.swing.JMenuBar();
        viewMenu = new javax.swing.JMenu();
        treeView = new javax.swing.JRadioButtonMenuItem();
        textView = new javax.swing.JRadioButtonMenuItem();
        mainPanel = new javax.swing.JPanel();
        treePanel = new javax.swing.JPanel();
        jSplitPane2 = new javax.swing.JSplitPane();
        treeScroll = new javax.swing.JScrollPane();
        tree = new javax.swing.JTree();
        nodePane = new javax.swing.JPanel();
        propertiesPanel = new javax.swing.JPanel();
        buttonPanel = new javax.swing.JPanel();
        okButton = new javax.swing.JButton();
        cancelButton = new javax.swing.JButton();
        textPanel = new javax.swing.JPanel();
        jScrollPane2 = new javax.swing.JScrollPane();
        textPane = new javax.swing.JTextPane();

        headerPanel.setLayout(new java.awt.BorderLayout());

        menuPanel.setLayout(new java.awt.BorderLayout());
        headerPanel.add(menuPanel, java.awt.BorderLayout.NORTH);
        menuPanel.add(menuBar);

        toolBarPanel.setPreferredSize(new java.awt.Dimension(39, 29));
        toolBarPanel.setLayout(new java.awt.BorderLayout());
        headerPanel.add(toolBarPanel, java.awt.BorderLayout.SOUTH);

        toolBar.setRollover(true);

        deleteButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/offset/nodes/client/view/resources/sc_delete.png"))); // NOI18N
        java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("offset/nodes/client/view/resources/ClientBundle"); // NOI18N
        deleteButton.setToolTipText(bundle.getString("tree.deleteTool")); // NOI18N
        deleteButton.setRolloverEnabled(true);
        deleteButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                deleteButtonremove(evt);
            }
        });
        toolBar.add(deleteButton);

        viewMenu.setText(bundle.getString("tree.viewMenu")); // NOI18N

        viewButtonGroup.add(treeView);
        treeView.setSelected(true);
        treeView.setText(bundle.getString("tree.viewMenu.tree")); // NOI18N
        treeView.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                showTree(evt);
            }
        });
        viewMenu.add(treeView);

        viewButtonGroup.add(textView);
        textView.setText(bundle.getString("tree.viewMenu.text")); // NOI18N
        textView.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                showTextualRepresentation(evt);
            }
        });
        viewMenu.add(textView);

        menuBar.add(viewMenu);

        setLayout(new java.awt.BorderLayout());

        mainPanel.setPreferredSize(new java.awt.Dimension(93, 326));
        mainPanel.setLayout(new java.awt.CardLayout());

        treePanel.setLayout(new java.awt.BorderLayout());

        jSplitPane2.setDividerLocation(250);

        tree.setBorder(javax.swing.BorderFactory.createEmptyBorder(10, 10, 1, 1));
        treeScroll.setViewportView(tree);

        jSplitPane2.setLeftComponent(treeScroll);

        nodePane.setLayout(new java.awt.BorderLayout());

        propertiesPanel.setLayout(new java.awt.CardLayout());
        nodePane.add(propertiesPanel, java.awt.BorderLayout.CENTER);

        okButton.setText(bundle.getString("general.ok")); // NOI18N
        okButton.setPreferredSize(new java.awt.Dimension(67, 23));
        okButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                ok(evt);
            }
        });

        cancelButton.setText(bundle.getString("general.cancel")); // NOI18N
        cancelButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                cancel(evt);
            }
        });

        javax.swing.GroupLayout buttonPanelLayout = new javax.swing.GroupLayout(buttonPanel);
        buttonPanel.setLayout(buttonPanelLayout);
        buttonPanelLayout.setHorizontalGroup(
            buttonPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, buttonPanelLayout.createSequentialGroup()
                .addContainerGap(111, Short.MAX_VALUE)
                .addComponent(okButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(cancelButton)
                .addContainerGap())
        );
        buttonPanelLayout.setVerticalGroup(
            buttonPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, buttonPanelLayout.createSequentialGroup()
                .addContainerGap(43, Short.MAX_VALUE)
                .addGroup(buttonPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(cancelButton)
                    .addComponent(okButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap())
        );

        nodePane.add(buttonPanel, java.awt.BorderLayout.SOUTH);

        jSplitPane2.setRightComponent(nodePane);

        treePanel.add(jSplitPane2, java.awt.BorderLayout.CENTER);

        mainPanel.add(treePanel, "tree");

        textPanel.setLayout(new java.awt.BorderLayout());

        jScrollPane2.setViewportView(textPane);

        textPanel.add(jScrollPane2, java.awt.BorderLayout.CENTER);

        mainPanel.add(textPanel, "text");

        add(mainPanel, java.awt.BorderLayout.CENTER);
    }// </editor-fold>//GEN-END:initComponents
    private void ok(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ok
        TreePath path = tree.getSelectionModel().getLeadSelectionPath();
        if (path == null) {
            return;
        }

        DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
        DetailPanelTreeNodeType nodeType = findNodeType(node);

        treeModel.acceptNodeModification(nodeType);
        ((DefaultTreeModel) tree.getModel()).nodeChanged(node);
}//GEN-LAST:event_ok

    private void cancel(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancel
        TreePath path = tree.getSelectionModel().getLeadSelectionPath();
        if (path == null) {
            return;
        }

        DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
        DetailPanelTreeNodeType nodeType = findNodeType(node);

        treeModel.cancelNodeModification(nodeType);
}//GEN-LAST:event_cancel

    private void deleteButtonremove(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteButtonremove
        if (tree.getSelectionPath() == null) {
            return;
        }

        DefaultMutableTreeNode remove = (DefaultMutableTreeNode) tree.getSelectionPath().getLastPathComponent();
        DefaultMutableTreeNode root = (DefaultMutableTreeNode) ((DefaultTreeModel) tree.getModel()).getRoot();
        if (remove == root) {
            return;
        }

        treeModel.removeNode(remove);
    }//GEN-LAST:event_deleteButtonremove

    private void showTree(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showTree
        ((CardLayout) mainPanel.getLayout()).show(mainPanel, "tree");
}//GEN-LAST:event_showTree

    private void showTextualRepresentation(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showTextualRepresentation
        Document document = textPane.getDocument();
        try {
            document.remove(0, document.getLength());
            treeModel.writeTextualRepresentation(document, ResourceBundle.getBundle("offset/nodes/client/dialog/view/DialogBundle"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        ((CardLayout) mainPanel.getLayout()).show(mainPanel, "text");
}//GEN-LAST:event_showTextualRepresentation

    public JTree getTree() {
        return tree;
    }

    public String getText() {
        Document document = textPane.getDocument();
        try {
            document.remove(0, document.getLength());
            treeModel.writeTextualRepresentation(document, ResourceBundle.getBundle("offset/nodes/client/dialog/view/DialogBundle"));
            return document.getText(0, document.getLength());
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * Return the result of the dialog operation
     * @return the result of the dialog: success or failure
     */
    public int getReturnStatus() {
        return returnStatus;
    }

    /**
     * Set the result of the dialog operation
     * @param returnStatus the result
     */
    public void setReturnStatus(int returnStatus) {
        this.returnStatus = returnStatus;
    }

    /**
     * Enable the ok button. This method will be called from within the node type panel. However, as the ok and cancel buttons are common
     * to all such panels, they are part of the container dialog. This method is used to access them from the panels.
     * @param enable enable or not
     */
    public void enableOkButton(boolean enable) {
        okButton.setEnabled(enable);
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JPanel buttonPanel;
    private javax.swing.JButton cancelButton;
    private javax.swing.JButton deleteButton;
    private javax.swing.JPanel headerPanel;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JSplitPane jSplitPane2;
    private javax.swing.JPanel mainPanel;
    private javax.swing.JMenuBar menuBar;
    private javax.swing.JPanel menuPanel;
    private javax.swing.JPanel nodePane;
    private javax.swing.JButton okButton;
    private javax.swing.JPanel propertiesPanel;
    private javax.swing.JTextPane textPane;
    private javax.swing.JPanel textPanel;
    private javax.swing.JRadioButtonMenuItem textView;
    private javax.swing.JToolBar toolBar;
    private javax.swing.JPanel toolBarPanel;
    private javax.swing.JTree tree;
    private javax.swing.JPanel treePanel;
    private javax.swing.JScrollPane treeScroll;
    private javax.swing.JRadioButtonMenuItem treeView;
    private javax.swing.ButtonGroup viewButtonGroup;
    private javax.swing.JMenu viewMenu;
    // End of variables declaration//GEN-END:variables
}
